/*
 * mox_module.c
 *
 *  Created on: Jul 11, 2010
 *      Author: jmonk
 */
#include <xs1.h>
#include <print.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "mox.h"
#include "mox_slave.h"
#include "mox_storage.h"
#include "mox_module.h"
#include "mox_threads.h"

int load_module(mox_master_state *state, const char module_name[])
{
	mox_sh id;
	struct mox_smod *mod;
	void *p;
	int i;

	if (!state->storage) return STORAGE_NONE;
	if (module_name[0] == '\0') return MOD_INVALID;
	while (!all_storage_free(state->storage));
	lock_all_storage(state->storage);

	for (i = 0; i < state->storage->num; i++) {
		if ((mod = check_module(state->storage->mod[i], module_name,
				(int)state->storage->prog_addr[i], 0x400))) break;
		unlock_storage(state->storage->mod[i]);
	}

	if (i < state->storage->num) {
		id = (mox_sh) i;
		for (i = (i+1); i < state->storage->num; i++) {
			unlock_storage(state->storage->mod[i]);
		}

		if (get_mod_storage(mod, state->storage->mod[id], &p)) {
			unlock_storage(state->storage->mod[id]);
			return MOX_OOM;
		}
		unlock_storage(state->storage->mod[id]);

		start_module(mod, id, state, p);

		free(p);

		return SUCCESS;
	} else {
		return MOD_INVALID;
	}
}

int init_module_start(mox_master_state *state, int core_num,
		struct module_at_start *mod, char *name)
{
	int response;
	int i;
	mox_msg_t type = TH_TYPE_APP;

	return mox_send_init_crth(state, mod->prog_space, core_num, mod->stack_size, name);
}

struct mox_smod *check_module(struct mox_module *mox_mod,
		const char module_name[], int addr, int func_offset)
{
	char *p;
	struct mox_smod *smod;
	int i, *x;
	int num;
	int *old = NULL;
	num = 0;
	do {
		if (old) free(old);
		x = read_storage(mox_mod, addr+num, 1);
		if (!x) {
			printstrln("Error");
			break;
		}
		old = x;
		num++;
	} while (*x == 0);
	if (old) free(old);

	p = (char *)read_storage(mox_mod, (int)(addr+func_offset), num*sizeof(struct mox_smod));

	for (i = 0; i < num; i++) {
		if (!strncmp(module_name, p+(i*sizeof(struct mox_smod)), 7)) {
			break;
		}
	}
	if (i < num) {
		smod = malloc(sizeof(struct mox_smod));
		memcpy(smod, p+(i*sizeof(struct mox_smod)), sizeof(struct mox_smod));
		free(p);
		return smod;
	} else {
		return NULL;
	}


}

int get_mod_storage(struct mox_smod *mod, struct mox_module *mox_mod, void **p)
{
	if (!(*p = (int*)read_storage(mox_mod, (int)mod->start_addr[0], mod->size))) {
		return 1;
	}
	return 0;
}

int start_module(struct mox_smod *mod, mox_sh id,
		mox_master_state *state, void *p)
{
	int i,j;
	int response;
	mox_msg_t type;
	unsigned flags[NUM_CORES];
	unsigned array[5];
	mox_msg *msg = malloc(sizeof(mox_msg));

	if (!msg) return 1;

	for (i = 0; i < (NUM_CORES - 1); i++) {
		if (mod->start_addr[i + 1] - mod->start_addr[i]) {
			flags[i] = 1;
		} else {
			flags[i] = 0;
		}
	}
	if (((mod->start_addr[NUM_CORES-1] - mod->start_addr[0])*sizeof(int)) < mod->size) {
		flags[NUM_CORES] = 1;
	} else {
		flags[NUM_CORES] = 0;
	}

	for (i = 0; i < NUM_CORES; i++) {
		if (flags[i]) {
			msg->type = MOX_CREATE_TH;
			if (i < NUM_CORES) {
				array[0] = mod->start_addr[i + 1] - mod->start_addr[i];
			} else {
				array[0] = mod->size - (mod->start_addr[NUM_CORES - 1] - mod->start_addr[0]);
			}
			array[1] = mod->const_addr[i];
			array[2] = mod->data_addr[i];
			array[3] = mod->stack[i];
			array[4] = mod->offset[i];
			msg->size = 5 * sizeof(unsigned);
			msg->content = array;
			mox_outct(state->chan_mox[i], MOX_MSG);
			mox_comm_msg_send(state->chan_mox[i], *msg);

			msg->size =  array[0];
			msg->content = (unsigned char *)p;
			p = (((unsigned char *)p) + array[0]);
			mox_outct(state->chan_mox[i], MOX_MSG);
			mox_comm_msg_send(state->chan_mox[i], *msg);
		}
	}

	for (i = 0; i < NUM_CORES; i++) {
		if (flags[i]) {
			if (msg->content) free(msg->content);
			free(msg);
			msg = mox_comm_msg_recv(state->chan_mox[i]);
			type = TH_TYPE_APP;
			if (msg->type == MOX_ACK) {
				if (msg->content) {
					response = ((unsigned *)msg->content)[0];
				} else {
					continue;
				}
				if (response == MOD_STORAGE) {
					state->storage->prog_addr[state->storage->num] = (int)0x40000;
					state->storage->mod[state->storage->num] = (struct mox_module*) malloc(sizeof(struct mox_module));
					state->storage->mod[state->storage->num]->core_id = state->chan_mox[i];
					state->storage->mod[state->storage->num++]->mod_id = ((unsigned *)msg->content)[1];
					type = TH_TYPE_MOD;
				} else if (response == MOD_GEN) {
					type = TH_TYPE_MOD;
				} else if (response == MOX_ACK) {
					continue;
				}
				state->running_thds[state->thd_cnt].thd_info = mod;
				state->running_thds[state->thd_cnt].type = type;
				state->thd_core[state->thd_cnt] = i;

				((unsigned *)msg->content)[0] = state->thd_cnt++;
				msg->size = sizeof(unsigned);
				mox_outct(state->chan_mox[i], MOX_MSG);
				mox_comm_msg_send(state->chan_mox[i], *msg);
			}
		}
	}

	if (msg->content) free(msg->content);
	free(msg);


	return SUCCESS;
}

void *m_malloc(chanend mox, int n)
{
	void *p;
	mox_chan_in_wd(mox);
	mox_chan_out_wd(mox, S_MALLOC);
	mox_chan_out_wd(mox, n);
	p = (void *)mox_chan_in_wd(mox);
	return p;
}

void m_free(chanend mox, void *p)
{
	mox_chan_in_wd(mox);
	mox_chan_out_wd(mox, S_FREE);
	mox_chan_out_wd(mox, (int)p);
}

int mod_thd_start_release(chanend mod, struct mox_slave_state *state)
{
	unsigned type;
	mox_msg *msg = malloc(sizeof(mox_msg));
	unsigned response;
	unsigned resp[0];
	unsigned resp_ct = 0;
	unsigned flag = 7;
	mox_msg_t mox_resp;

	if (!msg) printstrln("Serious Issue in mod_thd_start_release");
	msg->content = NULL;
	msg->size = 0;
	msg->type = TH_REQ_INFO;

	mox_comm_msg_send(mod, *msg);
	free(msg);
	msg = mox_comm_msg_recv(mod);
	if (msg->type == TH_PARAM) {
		type = ((unsigned *)msg->content)[0];
		if (type == TH_TYPE_MOD) {
			state->mod[state->num_mod] = mod;
			response = ((unsigned *)msg->content)[1];
			if (response == MOD_STORAGE) {
				resp[resp_ct++] = MOD_STORAGE;
				resp[resp_ct++] = state->num_mod;
			} else {
				resp[resp_ct++] = MOD_GEN;
			}
			state->num_threads += ((unsigned *)msg->content)[2];
			flag = 0;
		} else {
			state->num_threads += ((unsigned *)msg->content)[1];
			state->threads[state->num_thd] = mod;
			resp[resp_ct++] = TH_TYPE_APP;
			resp[resp_ct++] = 0;

			//thread type specific code here
		}
		free(msg->content);
		msg->content = (unsigned char *)resp;
		msg->size = 4 * resp_ct;
		msg->type = TH_TYPE_MOD;
		mox_comm_msg_send(state->mast, *msg);
		free(msg);
		while (mox_inct(state->mast) != MOX_MSG) mox_chan_out_wd(state->mast, MOX_NACK);
		msg = mox_comm_msg_recv(state->mast);

		state->thd_ids[flag?(7 + state->num_thd++):state->num_mod++] = ((unsigned *)msg->content)[0];
		msg->type = TH_RELEASE;
		mox_comm_msg_send(mod, *msg);

		free(msg->content);
		free(msg);

		return 0;
	} else {
		if (msg->content) free(msg->content);
		free(msg);
		return 1;
	}
}
